﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FuncWorks.XNA.XTiled;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Ruminate.GUI.Framework;
using System.IO;
using Ruminate.GUI.Content;
using Microsoft.Xna.Framework.Input;
using fess.Graphics;
using fess.Maps;
using fess.RPG;

namespace fess.Screens
{
  class Adventure : IScreen
  {
    const double ScrollSpeed = 0.5, ScrollEndPercent = 0.3;
    const float AutoScrollSpeed = 0.3f;

    GenericMap map;

    Rectangle mapView;
    Vector2 mapViewSpeed = Vector2.Zero;

    List<IActive> uiActivities = new List<IActive>();
    StackPanel charPics, enemyPics, messages;
    Panel interaction;
    Label interactionText;

    FessGame game;

    public Adventure(FessGame game)
    {
      this.game = game;
      mapView = game.GraphicsDevice.Viewport.Bounds;

      charPics = new StackPanel(new Rectangle(600, 0, 200, 140), game.gui);
      uiActivities.Add(charPics);
      enemyPics = new StackPanel(new Rectangle(600, 160, 200, 320), game.gui, true);
      uiActivities.Add(enemyPics);
      messages = new StackPanel(new Rectangle(200, 0, 400, 100), game.gui);
      uiActivities.Add(messages);

      interaction = new Panel(0, 452, 250, 28);
      interactionText = new Label(0, 0, "");
      interaction.AddWidget(interactionText);
      game.gui.AddWidget(interaction);
      interaction.Visible = false;
    }

    void DropAllMessages()
    {
      messages.DropAll();
    }

    void CharAdded(ActionCharacter character, bool enemy)
    {
      var pics = enemy ? enemyPics : charPics;
      if (!pics.children.Any(p => ((CharPic)p).Character == character))
        pics.Add(new CharPic(character), character == game.fess);
    }

    void CharRemoved(ActionCharacter character, bool enemy)
    {
      var pics = enemy ? enemyPics : charPics;
      for(int i = 0; i < pics.children.Count; ++i)
        if (((CharPic)pics.children[i]).Character == character)
        {
          pics.RemoveAt(i);
          return;
        }
    }

    public void SwitchMap(GenericMap newMap)
    {
      if (map != null)
      {
        map.DropMessages -= DropAllMessages;
        map.charAdded -= CharAdded;
        map.charRemoved -= CharRemoved;
        enemyPics.DropAll();
        charPics.DropAll();
      }

      map = newMap;
      mapView.Location = (game.fess.Character.Position - (mapView.Center.ToVector2() - mapView.Location.ToVector2())).ToPoint();
      mapView.X = Math.Max(0, mapView.X);
      mapView.Y = Math.Max(0, mapView.Y);
      map.DropMessages += DropAllMessages;
      map.charAdded += CharAdded;
      map.charRemoved += CharRemoved;

      foreach (var c in map.toShow)
        CharAdded(c, false);

      foreach (var c in map.enemiesToShow)
        CharAdded(c, true);
    }

    void MoveCameraConstrained(Point newPos, Rectangle charRect)
    {
      bool constrainBounds = map.Bounds.Contains(mapView);

      Rectangle newView = mapView;
      newView.X = newPos.X;
      if ((!constrainBounds && newView.X >= 0 && newView.Y >= 0 || map.Bounds.Contains(newView)) && newView.Contains(charRect))
        mapView = newView;
      else
        newView = mapView;

      newView.Y = newPos.Y;
      if ((!constrainBounds && newView.X >= 0 && newView.Y >= 0 || map.Bounds.Contains(newView)) && newView.Contains(charRect))
        mapView = newView;
      else
        newView = mapView;
    }

    public bool Update(GameTime gameTime)
    {
      map.mapView = mapView;

      if (!map.Update(gameTime))
        return false;

      foreach (var msg in map.messages)
        messages.Add(msg);

      interaction.Visible = true;
      if (map.interaction != null)
        ShowInteraction("[E]: " + map.interaction);
      else if (game.IsInDialog)
        ShowInteraction("[Space]: Пропустить");
      else
        interaction.Visible = false;

      if (!game.IsInDialog && messages.Count > 0 && Misc.IsKeyJustPressed(Keys.Space))
        messages.Pop();

      map.messages.Clear();

            // Allows the game to exit
      if (Misc.IsKeyJustPressed(Keys.Escape))
      {
        game.Exit();
        return false;
      }

      MouseState mouse = Mouse.GetState();
      Vector2 pointed = new Vector2(mouse.X + mapView.X, mouse.Y + mapView.Y);
      if (game.GraphicsDevice.Viewport.Bounds.Contains(new Point(mouse.X, mouse.Y)) && !game.gui.HasMouse && !game.fess.Dead)
      {
        if (mouse.RightButton == ButtonState.Pressed)
          map.Clicked(pointed, false);
        if (mouse.LeftButton == ButtonState.Pressed)
          map.Clicked(pointed, true);
      }
    
      var keys = Keyboard.GetState();

      Point newPos = mapView.Location;
      int scrollAmount = (int)(gameTime.ElapsedGameTime.TotalMilliseconds * ScrollSpeed);
      if (keys.IsKeyDown(Keys.Down))
        newPos.Y += scrollAmount;

      if (keys.IsKeyDown(Keys.Up))
        newPos.Y -= scrollAmount;

      if (keys.IsKeyDown(Keys.Right))
        newPos.X += scrollAmount;

      if (keys.IsKeyDown(Keys.Left))
        newPos.X -= scrollAmount;

      if (newPos != mapView.Location)
      {
        mapViewSpeed = Vector2.Zero;
        MoveCameraConstrained(newPos, map.CharRect);
      }

      if (map.justMoved || mapViewSpeed.X != 0 || mapViewSpeed.Y != 0)
      {
        if (game.fess.Character.Position.X - mapView.Left < mapView.Width * 0.1)
          mapViewSpeed.X = -AutoScrollSpeed;
        else if (mapViewSpeed.X < 0 && game.fess.Character.Position.X - mapView.Left > mapView.Width * ScrollEndPercent)
          mapViewSpeed.X = 0;

        if (mapView.Right - 200 - game.fess.Character.Position.X < mapView.Width * 0.1)
          mapViewSpeed.X = AutoScrollSpeed;
        else if (mapViewSpeed.X > 0 && mapView.Right - 200 - game.fess.Character.Position.X > mapView.Width * ScrollEndPercent)
          mapViewSpeed.X = 0;

        if (game.fess.Character.Position.Y - mapView.Top - 80 < mapView.Height * 0.1)
          mapViewSpeed.Y = -AutoScrollSpeed;
        else if (mapViewSpeed.Y < 0 && game.fess.Character.Position.Y - mapView.Top - 80 > mapView.Height * ScrollEndPercent)
          mapViewSpeed.Y = 0;

        if (mapView.Bottom - game.fess.Character.Position.Y < mapView.Height * 0.1)
          mapViewSpeed.Y = AutoScrollSpeed;
        else if (mapViewSpeed.Y > 0 && mapView.Bottom - game.fess.Character.Position.Y > mapView.Height * ScrollEndPercent)
          mapViewSpeed.Y = 0;
      }

      newPos = mapView.Location;
      newPos.X += (int)(gameTime.ElapsedGameTime.TotalMilliseconds * mapViewSpeed.X);
      newPos.Y += (int)(gameTime.ElapsedGameTime.TotalMilliseconds * mapViewSpeed.Y);
      MoveCameraConstrained(newPos, map.CharRect);

      for (int i = 0; i < uiActivities.Count; ++i)
        if (!uiActivities[i].Update(game.time))
        {
          uiActivities.RemoveAt(i);
          --i;
        }

      game.gui.Update();
      return true;
    }

    private void ShowInteraction(string message)
    {
      interactionText.Value = message;
      interactionText.Layout();
      var area = interaction.Area;
      area.Width = interactionText.Area.Width + 8;
      interaction.Area = area;
      interaction.AbsoluteArea = area;
    }

    public void Draw(GameTime gameTime)
    {
      map.mapView = mapView;
      map.Draw();
      game.gui.Draw();
    }
  }
}
